package com.tianji.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.JsonUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.config.SpringAIConfig;
import com.tianji.constants.Constant;
import com.tianji.entity.pojo.ChatSession;
import com.tianji.entity.vo.ChatEventVO;
import com.tianji.enums.ChatEventTypeEnum;
import com.tianji.memory.RedisChatMemory;
import com.tianji.service.ChatService;
import com.tianji.tools.result.CourseInfo;
import com.tianji.tools.result.PrePlaceOrder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.QuestionAnswerAdvisor;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static java.lang.Thread.sleep;

@Slf4j
@Service
public class ChatServiceImpl  implements ChatService {

    @Autowired
    private ChatClient chatClient;
    @Autowired
    private RedisChatMemory redisChatMemory;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private VectorStore vectorStore;
    @Autowired
    private ChatSessionServiceImpl chatSessionService;

    //定义字符串用来存储中断的消息记录
    private StringBuilder sb = new StringBuilder();

    //key放sessionId，value：当前是否继续输出的标识
    //如果是分步式环境的话，
    private static final Map<String, Boolean> FLAG_MAP = new ConcurrentHashMap<>();

    @Override
    public Flux<ChatEventVO> chat(String question, String sessionId) {

        String conversationId = getConversationId(UserContext.getUser() + "", sessionId);

        //生成请求id
        String requestId = IdUtil.fastSimpleUUID();
        //准备一个Map,当作toolcall的上下文
        Map<String, Object> map = new HashMap<>();
        map.put("requestId", requestId);
        map.put("userId", UserContext.getUser() + "");

        //添加标题，这里的策略是每次都判断执行
        //先获取一下标题，偷懒直接截取question ,
        // 优化成异步执行？好处是什么？ 同步的话有什么问题？
        chatSessionService.updateTitle(sessionId, StrUtil.subPre(question, 100),UserContext.getUser());

        return chatClient.prompt()
                .system(SpringAIConfig.SYSTEM_PROMPT)
                .toolContext(map) //传入map当作toolcall的上下文
                .advisors(advisor -> advisor
                        .advisors(new QuestionAnswerAdvisor(vectorStore, org.springframework.ai.vectorstore.SearchRequest.builder().query("").build()))
                        .param(AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, conversationId))
                .user(question)
                .stream()
                .chatResponse()
                //流第一次执行，大模型返回第一个token的时候
                .doFirst(() -> {
                    log.info("doFirst开始执行,第一次", sessionId);
                    FLAG_MAP.put(sessionId, true);
                })
                //完成的时候
                .doOnComplete(() -> {
                    log.info("doOnComplete执行完成，完成", sessionId);
                    FLAG_MAP.remove(sessionId);
                })
                //出现异常时
                .doOnError(throwable -> {
                    log.info("doOnError执行异常，异常", sessionId);
                    FLAG_MAP.remove(sessionId);
                })
                //用户主动中断时，保存中断的消息记录
                .doOnCancel(new Runnable() {
                    @Override
                    public void run() {
                        log.info("doOnCancel执行，用户主动中断", sessionId);
                        //保存中断的消息记录
                        redisChatMemory.add(conversationId, List.of(new AssistantMessage(sb.toString())));
                    }
                })
                // 输出过程中，判断是否正在输出，需要返回一个boolean类型，true就继续输出，false就停止
                .takeWhile(chatResponse -> {
                    //给个默认值，如果这个key不存在，就返回第二个参数即false
                    Boolean result = FLAG_MAP.getOrDefault(sessionId, false);
                    log.info("takeWhile执行，判断是否继续输出", sessionId, result);
                    return result;
                })
                //转换，将chatResponse对象转换成ChatEventVO对象
                .map(chatResponse -> {
                    //获取到输出的结果，将每次输出的结果拼接到StringBuilder中，在主动中断时存入结果
                    String text = chatResponse.getResult().getOutput().getText();
                    sb.append(text);
                    return ChatEventVO.builder()
                            .eventData(text)
                            .eventType(ChatEventTypeEnum.DATA.getValue())
                            .build();
                })
                //添加停止事件，标记输出结束
                .concatWith(Flux.defer(() -> getFinalEvents(requestId)));


    }

    @Override
    public void stop(String id) {
        FLAG_MAP.remove(id);
        log.info("stop执行，停止输出", id);
    }

    public String getConversationId(String id, String sessionId) {
        return id + ":" + sessionId;
    }

    public Flux<ChatEventVO> getFinalEvents(String requestId) {
        //1. 首先检查是否有课程卡片需要发送
        String JsonStr = (String) stringRedisTemplate.opsForHash().get(Constant.Key_COURSE_SEARCH, requestId);
        //2.检查是否有订单卡片需要发送
        String OrderStr = (String) stringRedisTemplate.opsForHash().get(Constant.PREFIX_OREDER, requestId);

        // 使用变量存储要返回的事件流
        if (JsonStr != null) {//调用了Tools,需要添加卡片信息
            //构建返回卡片信息，按照前端的格式封装好
            CourseInfo bean = JsonUtils.toBean(JsonStr, CourseInfo.class);
            HashMap<String, Object> toolsMap = new HashMap<>();
            toolsMap.put("courseInfo_" + bean.getId(), bean);
            return Flux.just(ChatEventVO.builder()
                            .eventData(toolsMap)
                            .eventType(ChatEventTypeEnum.PARAM.getValue())
                            .build())
                    .concatWith(Flux.just(ChatEventVO.builder()
                            .eventType(ChatEventTypeEnum.STOP.getValue())
                            .build()));
        }
        if (OrderStr != null) {//订单卡片发送逻辑
            PrePlaceOrder bean = JsonUtils.toBean(OrderStr, PrePlaceOrder.class);
            log.info("OrderStr:{}", OrderStr);
            log.info("PreOrder:{}", bean);
            HashMap<String, Object> map = new HashMap<>();
            map.put("prePlaceOrder", bean);
            return Flux.just(ChatEventVO.builder()
                            .eventData(map)
                            .eventType(ChatEventTypeEnum.PARAM.getValue())
                            .build())
                    .concatWith(Flux.just(ChatEventVO.builder()
                            .eventType(ChatEventTypeEnum.STOP.getValue())
                            .build()));
        }
        // 直接发送停止事件
        return Flux.just(ChatEventVO.builder()
                .eventType(ChatEventTypeEnum.STOP.getValue())
                .build());

    }


}